﻿using CPAIS.Model;
using CPAIS.Updater;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace CPAIS.Adapter
{
    public class TestCPMAnalysisSpeedAdapter
    {
        private class SepratedLogModel : LogModel
        {
            public static SepratedLogModel[] SeprateLogModel(LogModel logModel, int parts)
            {
                SepratedLogModel[] logModels = new SepratedLogModel[parts];

                int count = logModel.TraceCount;
                int avgSize = count / parts;
                for (int i = 0; i < parts; i++)
                {
                    logModels[i] = new SepratedLogModel();
                    logModels[i].Valid = false;
                    logModels[i].LogName = string.Format("{0}_{1}", logModel.LogName, i);
                    for (int j = 0; j < avgSize * (i + 1); j++)
                    {
                        CPAIS.Model.Trace trace = logModel.Traces[j];
                        CPAIS.Model.Trace tt = new CPAIS.Model.Trace(logModels[i], trace.ID);
                        tt.Sex = trace.Sex;
                        tt.Department = trace.Department;
                        tt.Cost = trace.Cost;
                        tt.Birthday = trace.Birthday;
                        tt.Admit = trace.Admit;
                        tt.Discharge = trace.Discharge;
                        foreach (var action in trace.Actions)
                        {
                            ActionEvent ae = null;
                            if (action.IsTimestampAvailable)
                                ae = new ActionEvent(tt, action.Timestamp, action.TypeName);
                            else
                                ae = new ActionEvent(tt, action.Day, action.TypeName);
                            tt.AddAction(ae);
                        }
                        foreach (var observation in trace.Observations)
                        {
                            ObservationEvent ae = null;
                            if (observation.IsTimestampAvailable)
                                ae = new ObservationEvent(tt, observation.Timestamp, observation.TypeName);
                            else
                                ae = new ObservationEvent(tt, observation.Day, observation.TypeName);
                            tt.AddObservation(ae);
                        }
                        var diagnoses = trace.Diagnoses;
                        foreach (var diagnosis in diagnoses)
                            tt.AddDiagnosis(diagnosis);

                        tt.OrderTrace();
                        logModels[i].AddTrace(tt);
                    }
                    if (logModels[i].TraceCount != 0)
                        logModels[i].Valid = true;
                }

                return logModels;
            }
        }

        private class AnalysisUpdater : ProgressUpdater
        {
            private ProgressUpdater outUpdater;

            public AnalysisUpdater(ProgressUpdater outUpdater)
            {
                this.outUpdater = outUpdater;
            }

            protected override bool OnUpdateProgress(double progress)
            {
                if (outUpdater != null)
                    outUpdater.UpdateProgress(progress);
                return true;
            }

            public void UpdateMessage_P(string message)
            {
                if (outUpdater != null)
                    outUpdater.UpdateMessage(message);
            }

            public ProgressUpdater OutUpdater { get { return outUpdater; } }
        }

        private long[] timeElapsedWithTraceCount;
        private int[] traceCountWithTraceCount;
        private long[] timeElapsedWithTopicNo;

        private LogModel logModel;
        private Stopwatch stopwatch;
        private AnalysisUpdater analysisUpdater;

        public TestCPMAnalysisSpeedAdapter(LogModel logModel)
        {
            this.logModel = logModel;
            this.stopwatch = new Stopwatch();
        }

        public long[] TimeElapsedWithTraceCount { get { return timeElapsedWithTraceCount; } }
        public int[] TraceCountWithTraceCount { get { return traceCountWithTraceCount; } }

        public long[] TimeElapsedWithTopicNo { get { return timeElapsedWithTopicNo; } }
        public int TraceCountWithTopicNo { get { return logModel.TraceCount; } }

        public void Execute(int parts, int defaultTopicNo, int maxTopicNo, double alpha, double beta, double gamma, int iterations, ProgressUpdater updater)
        {
           try
            {
                this.traceCountWithTraceCount = new int[parts];
                int avgSize = logModel.TraceCount / parts;
                for (int i = 0; i < parts; i++)
                {
                    this.traceCountWithTraceCount[i] = avgSize * (i + 1);
                }

                this.analysisUpdater = new AnalysisUpdater(updater);
                this.analysisUpdater.OutUpdater.Begin();
                           
                // StartTestingTraceCount
                LogModel[] logModels = SepratedLogModel.SeprateLogModel(logModel, parts);
                timeElapsedWithTraceCount = new long[parts];
                for (int i = 0; i < parts; i++)
                {
                    analysisUpdater.UpdateMessage_P(string.Format("Trace count as variable ({0}/{1})", i + 1, parts));
                    if (logModels[i].Valid)
                    {
                        CPMGibbsAdapter adapter = new CPMGibbsAdapter(logModels[i]);
                        stopwatch.Reset();
                        stopwatch.Start();
                        adapter.Execute(defaultTopicNo, alpha, beta, gamma, iterations, analysisUpdater);
                        stopwatch.Stop();
                        timeElapsedWithTraceCount[i] = stopwatch.ElapsedMilliseconds;
                    }
                    else
                        timeElapsedWithTraceCount[i] = -1;
                }

                // StartTestingTopicNo
                timeElapsedWithTopicNo = new long[maxTopicNo];
                for (int i = 0; i < maxTopicNo; i++)
                {
                    analysisUpdater.UpdateMessage_P(string.Format("Topic count as variable ({0}/{1})", i + 1, maxTopicNo));                    
                    CPMGibbsAdapter adapter = new CPMGibbsAdapter(logModel);
                    stopwatch.Reset();
                    stopwatch.Start();
                    adapter.Execute(i + 1, alpha, beta, gamma, iterations, analysisUpdater);
                    stopwatch.Stop();
                    timeElapsedWithTopicNo[i] = stopwatch.ElapsedMilliseconds;
                }

                this.analysisUpdater.OutUpdater.Complete();
            }
            catch (Exception ex)
            {
                if (analysisUpdater.OutUpdater.State == UpdateState.Cancelled)
                    return;
                else
                    analysisUpdater.OutUpdater.Fail(ex.Message);
            }
        }
    }
}
